#include "udp.h"
#include <ctype.h>
//isalpha函数在<ctype.h>中
#include <sqlite3.h>
#include <time.h>

#define DATABASE_NAME "mydatabase.db"

int callback(void *, int, char **, char **);//sqlite3_exec函数的回调函数，用于处理查询结果

void SaveRecord(const char *word, const char *buf);//用于将查询结果保存到文件中

/* 其他必要的环境已封装好了，只需要实现与客户端的交互即可
 * 这里的fd是服务端的socket，
 * addr是服务端的地址*/
void udp_main(const int fd, const struct sockaddr_in *addr) {
    int ret = 0, rc;
    struct sockaddr_in client_addr;
    socklen_t addrlen = sizeof(client_addr);
    //client_addr和addrlen用于存储客户端地址，便于发出数据回客户端
    sqlite3 *db;
    char buf[BUFSIZ] = {};
    char word[128] = {};
    char *sql_query, *errmsg;

    /* 1.打开数据库 */
    if( (rc = sqlite3_open(DATABASE_NAME, &db) ) ) {
        printf("[%s:%d]无法打开数据库: %s\n", __FUNCTION__, __LINE__, sqlite3_errmsg(db) );
        exit(0);
    }
    /* 2.循环处理客户端数据 */
    while(1) {
        /* 2.1.接收客户端数据 */
        do {
            ret = recvfrom(fd, buf, BUFSIZ, 0, (struct sockaddr *)&client_addr, &addrlen);
        }while(ret < 0 && errno == EINTR);
        if(ret < 0)
            ErrExit("recvfrom");

        printf("[%s:%d]收到的数据: {%s}\n", __FUNCTION__, __LINE__, buf);
        /* 2.2.提取需要翻译的单词 */
        for(ret = 0; isalpha(buf[ret]) || buf[ret] == ' '; ret++);
        /*
        //1.isalpha函数通常用于判断一个字符是否是字母。具体来说，它检查一个字符是否属于字母表中的字母，包括大写和小写字母。其返回值为：
        //1：参数为字母
        //0：参数不为字母
        //-1：发生错误
        //2.buf[ret] == ' '条件表示的是有些单词中间可能有空格，所以需要把空格也作为单词的一部分。
        */
        buf[ret] = '\0';
        strncpy(word, buf, strlen(buf));//使用word变量来存储需要翻译的单词
        /* 2.3.用SQL语句进行查询 */
        sql_query = sqlite3_mprintf("select * from dictionary where english like '%s'", buf);
        rc = sqlite3_exec(db, sql_query, callback, buf, &errmsg);
        if(rc != SQLITE_OK) {
            sprintf(buf, "查询失败:%s\n", errmsg);
            printf("[%s:%d]%s", __FUNCTION__, __LINE__, buf);
            sendto(fd, buf, strlen(buf) + 1, 0, (struct sockaddr *)&client_addr, addrlen);
            sqlite3_free(errmsg);
            continue;
        }
        sqlite3_free(sql_query);
        printf("[%s:%d]查询结果: {%s}\n", __FUNCTION__, __LINE__, buf);//buf中就是查询到的结果
        SaveRecord(word, buf);//将查询到的结果保存下来
        sendto(fd, buf, strlen(buf) + 1, 0, (struct sockaddr *)&client_addr, addrlen);

    }
    /* 3.关闭数据库，关闭fd，并且退出程序 */
    sqlite3_close(db);
    close(rc);
    close(fd);
}

//NotUsed是sqlite3_exec函数传入的参数,argc是查询到的值的数量,argv是查询到的值,ColName是查询到的列名
int callback(void *NotUsed, int argc, char **argv, char **ColName) {
    char *buf = NotUsed;//NotUsed是sqlite3_exec函数传入的参数，用于传递buf
    if(argc == 2) {
        /* 把查询到的字符串复制给buf */
        strncpy(buf, argv[1], strlen(argv[1]) + 1 );
        //argv[0]是需要查询的英文字符串
        //argv[1]是查询到的中文字符串，strlen(argv[1]) + 1 是argv[1]的长度加1，用于防止缓冲区溢出
    } else
        buf[0] = '\0'; //如果失败就将字符串置空
    /* 将换行符替换为'\0' */
    /*字符串增加换行符，以便客户端可以正确解析*/
    buf[strlen(argv[1])] = '\n';
    buf[strlen(argv[1])+1] = '\0';
    return 0;
}

void SaveRecord(const char *word, const char *buf) {
    FILE *fp;
    time_t ctime;
    struct tm *ctimestr;
    int linecount = 0;
    char str[128] = {};
    if( (fp = fopen("recode.txt", "a+") ) == NULL)
        ErrExit("fopen");
    while( fgets(str, 128, fp) != NULL) {
        if(str[strlen(str) - 1] == '\n') {
            linecount++;
        }
    }
    ctime = time(NULL);
    ctimestr = localtime(&ctime);
    // printf("%d\t%04d-%02d-%02d %02d:%02d:%02d\t%s\t%s\n",
    //         linecount,
    //         ctimestr->tm_year+1900, ctimestr->tm_mon+1, ctimestr->tm_mday,
    //         ctimestr->tm_hour, ctimestr->tm_min, ctimestr->tm_sec, word, buf);
    fprintf(fp, "%d\t%04d-%02d-%02d %02d:%02d:%02d\t%s\t%s\n",
            linecount,
            ctimestr->tm_year+1900, ctimestr->tm_mon+1, ctimestr->tm_mday,
            ctimestr->tm_hour, ctimestr->tm_min, ctimestr->tm_sec, word, buf);
    linecount++;
    fclose(fp);
}